/**
	Pulsar engine. Core.
	Windows threads

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

#include <process.h>
#include <pulsar/pulsar.h>
#include <pulsar/src/pulsar_intr.h>

namespace pl
{

class ThreadHandlerWin : public ThreadHandler
{
public:
	explicit ThreadHandlerWin(Thread* ptr, int stackSize, const pl_char* name);
	virtual ~ThreadHandlerWin();

	virtual bool setThreadProcessor(int index);
	virtual bool setThreadPriority(ThreadPrior prior, int order);
	virtual bool isThreadRunning();
	virtual bool isThreadValid();

private:
	HANDLE _hThread;
};

/**
	Thread entry point
*/
static unsigned int __stdcall _threadProcWin(void* lpParameter)
{
	Thread* t = static_cast<Thread*>(lpParameter);
	t->_threadProc();
	_endthreadex(0);
	return 0;
}

} // namespace pl

pl_uintptr pl::getThreadId()
{
	return GetCurrentThreadId();
}
pl_uintptr pl::getThreadCount()
{
	return 1; // TODO
}
pl_uintptr pl::getProcessId()
{
	return GetCurrentProcessId();
}
void pl::sleepMs(pl_uint32 timeMs)
{
	Sleep(timeMs);
	return;
}

/*
*/
pl::ThreadHandler* pl::ThreadHandler::_createThread(Thread* ptr, int stackSize, const pl_char* name)
{
	return PL_NEW ThreadHandlerWin(ptr, stackSize, name);
}

/*
*/
pl::ThreadHandlerWin::ThreadHandlerWin(Thread* ptr, int stackSize, const pl_char* name)
{
	unsigned int threadId;

	_hThread = (HANDLE)(_beginthreadex(NULL, (unsigned int)stackSize, _threadProcWin, ptr, 0, &threadId));
	if (_hThread == NULL || _hThread == INVALID_HANDLE_VALUE)
	{
		plLogWarnA(PL_LOG_NAME, PL_T("Can't create thread, error code: ") << GetLastError());
		return;
	}
	return;
}

/*
*/
pl::ThreadHandlerWin::~ThreadHandlerWin()
{
	if (_hThread != NULL)
	{
		CloseHandle(_hThread);
	}
	return;
}

/*
*/
bool pl::ThreadHandlerWin::setThreadProcessor(int index)
{
	DWORD_PTR affinityMask = 1 << index;

	if (SetThreadAffinityMask(_hThread, affinityMask) == 0)
	{
		plLogWarnA(PL_LOG_NAME, PL_T("Can't set thread to processor, error code: ") << GetLastError());
		return false;
	}
	return true;
}

/*
*/
bool pl::ThreadHandlerWin::setThreadPriority(ThreadPrior prior, int order)
{
	int winPrior;

	switch (prior)
	{
		case THREAD_PRIOR_IDLE:
			winPrior = THREAD_PRIORITY_IDLE;
			break;
		case THREAD_PRIOR_LOW:
			winPrior = THREAD_PRIORITY_BELOW_NORMAL;
			break;
		case THREAD_PRIOR_NORMAL:
			winPrior = THREAD_PRIORITY_NORMAL;
			break;
		case THREAD_PRIOR_HIGH:
			winPrior = THREAD_PRIORITY_ABOVE_NORMAL;
			break;
		case THREAD_PRIOR_MAX:
			winPrior = THREAD_PRIORITY_TIME_CRITICAL;
			break;
		default:
			PL_ASSERTW(false, PL_T("Wrong call"));
			return false;
	}
	return SetThreadPriority(_hThread, winPrior) ? true : false;
}

/*
*/
bool pl::ThreadHandlerWin::isThreadValid()
{
	if (_hThread == NULL || _hThread == INVALID_HANDLE_VALUE)
	{
		return false;
	}
	return true;
}

/*
*/
bool pl::ThreadHandlerWin::isThreadRunning()
{
	if (!isThreadValid())
	{
		return false;
	}
	const DWORD res = ::WaitForSingleObject(_hThread, 0);
	return WAIT_TIMEOUT == res;
}

//
// End of file 'pulsar_thread_win.cpp'
//
